package com.wjk.kylin.core.security;

import com.alibaba.fastjson.JSONObject;
import com.wjk.kylin.core.common.redis.RedisRepository;
import com.wjk.kylin.core.constant.Constants;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;

import java.util.Date;

/**
 * Token 工具类
 *
 * @author hyd
 */
@Slf4j
public abstract class AbstractTokenUtil {


	/**
	 * redis repository
	 */
	@Autowired
	private RedisRepository redisRepository;


	/**
	 * 获取用户名
	 *
	 * @param token Token
	 * @return String
	 */
	public String getUsernameFromToken(String token) {
		Claims claims = getClaimsFromToken(token);
		return claims != null ? claims.getSubject() : null;
	}

	/**
	 * 获取过期时间
	 *
	 * @param token Token
	 * @return Date
	 */
	public Date getExpiredFromToken(String token) {
		Claims claims = getClaimsFromToken(token);
		return claims != null ? claims.getExpiration() : null;
	}

	/**
	 * 获得 Claims
	 *
	 * @param token Token
	 * @return Claims
	 */
	private Claims getClaimsFromToken(String token) {
		Claims claims;
		try {
			claims = Jwts.parser().setSigningKey(Constants.SECRET).parseClaimsJws(token).getBody();
		} catch (Exception e) {
			log.warn("getClaimsFromToken exception", e);
			claims = null;
		}
		return claims;
	}

	/**
	 * 计算过期时间
	 *
	 * @return Date
	 */
	private Date generateExpired() {
		return new Date(System.currentTimeMillis() + Constants.EXPIRATION * 1000);
	}

	/**
	 * 判断 Token 是否过期
	 *
	 * @param token Token
	 * @return Boolean
	 */
	private Boolean isTokenExpired(String token) {
		Date expirationDate = getExpiredFromToken(token);
		return expirationDate.before(new Date());
	}

	/**
	 * 生成 Token
	 *
	 * @param userDetails 用户信息
	 * @return String
	 */
	public String generateToken(UserDetails userDetails) {
		String token = Jwts.builder().setSubject(userDetails.getUsername()).setExpiration(generateExpired())
				.signWith(SignatureAlgorithm.HS512, Constants.SECRET).compact();

		String key = Constants.REDIS_PREFIX_AUTH + userDetails.getUsername();
		redisRepository.setExpire(key, token, Constants.EXPIRATION);
		putUserDetails(userDetails);
		return token;
	}

	/**
	 * 验证 Token
	 *
	 * @param token Token
	 * @return Boolean
	 */
	public Boolean validateToken(String token) {
		log.info("执行token验证的功能");
		final String username = getUsernameFromToken(token);
		String key = Constants.REDIS_PREFIX_AUTH + username;
		String redisToken = redisRepository.get(key);
		return StringUtils.isNotBlank(token) && !isTokenExpired(token) && token.equals(redisToken);
	}

	/**
	 * 移除 Token
	 *
	 * @param token Token
	 */
	public void removeToken(String token) {
		log.info("移除token" + token);
		removeTokenAndUser(getUsernameFromToken(token));
	}

	/**
	 * 获得用户信息 Json 字符串
	 *
	 * @param token Token
	 * @return String
	 */
	protected String getUserDetailsString(String token) {
		final String username = getUsernameFromToken(token);
		String key = Constants.REDIS_PREFIX_USER + username;
		return redisRepository.get(key);
	}

	/**
	 * 获得用户信息
	 *
	 * @param token Token
	 * @return UserDetails
	 */
	public abstract UserDetails getUserDetails(String token);

	/**
	 * 存储用户信息
	 *
	 * @param userDetails 用户信息
	 */
	private void putUserDetails(UserDetails userDetails) {
		String key = Constants.REDIS_PREFIX_USER + userDetails.getUsername();
		log.info("存储用户信息userDetails：{}", JSONObject.toJSONString(userDetails));
		redisRepository.setExpire(key,JSONObject.toJSONString(userDetails), Constants.EXPIRATION);
	}

	/**
	 * 移除 Token 以及用户信息
	 *
	 * @param username
	 */
	public void removeTokenAndUser(String username) {
		String tokenKey = Constants.REDIS_PREFIX_AUTH + username;
		redisRepository.del(tokenKey);

		String userKey = Constants.REDIS_PREFIX_USER + username;
		redisRepository.del(userKey);
	}

}